/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.AopInvocationException;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.DecoratingProxy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * JDK-based {@link AopProxy} implementation for the Spring AOP framework,
 * based on JDK {@link java.lang.reflect.Proxy dynamic proxies}.
 *
 * <p>Creates a dynamic proxy, implementing the interfaces exposed by
 * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods
 * defined in classes, rather than interfaces.
 *
 * <p>Objects of this type should be obtained through proxy factories,
 * configured by an {@link AdvisedSupport} class. This class is internal
 * to Spring's AOP framework and need not be used directly by client code.
 *
 * <p>Proxies created using this class will be thread-safe if the
 * underlying (target) class is thread-safe.
 *
 * <p>Proxies are serializable so long as all Advisors (including Advices
 * and Pointcuts) and the TargetSource are serializable.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @author Dave Syer
 * @author Sergey Tsypanov
 * @see java.lang.reflect.Proxy
 * @see AdvisedSupport
 * @see ProxyFactory
 */
//  AopProxy的子类。使用JDK的方式创建代理对象。它持有Advised对象
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 5531744639992436476L;


	/*
	 * NOTE: We could avoid the code duplication between this class and the CGLIB
	 * proxies by refactoring "invoke" into a template method. However, this approach
	 * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
	 * elegance for performance (we have a good test suite to ensure that the different
	 * proxies behave the same :-)).
	 * This way, we can also more easily take advantage of minor optimizations in each class.
	 */

	/** We use a static Log to avoid serialization issues. */
	private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

	/** Config used to configure this proxy. */
	private final AdvisedSupport advised;

	private final Class<?>[] proxiedInterfaces;

	/**
	 * Is the {@link #equals} method defined on the proxied interfaces?
	 */
	private boolean equalsDefined;

	/**
	 * Is the {@link #hashCode} method defined on the proxied interfaces?
	 */
	private boolean hashCodeDefined;


	/**
	 * Construct a new JdkDynamicAopProxy for the given AOP configuration.
	 * @param config the AOP configuration as AdvisedSupport object
	 * @throws AopConfigException if the config is invalid. We try to throw an informative
	 * exception in this case, rather than let a mysterious failure happen later.
	 */
	public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		//  如果不存在Advisor或者目标对象为空的话,抛出异常
		if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
		//  获取AdvisedSupport类型对象的所有接口
		// ====== 获取代理对象需要实现的接口（业务接口和内置接口）,不管他
		// ###### 设置JDK动态代理所要代理的接口
		this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		//  接口是否定义了equals和hashcode方法,正常是没有的
		// ====== 判断接口中是否重写了equals和hashCode方法，也不用搭理他
		findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
	}


	@Override
	public Object getProxy() {
		//  使用默认的类加载器
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	// ====== 获取最终的代理对象(由JDK生成；运行时织入)
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		/** 
		 *  创建代理对象 this是JdkDynamicAopProxy
		 *  JdkDynamicAopProxy同时实现了InvocationHandler接口
		 *  这里我们生成的代理对象可以向上造型为任意proxiedInterfaces中的类型
		 */
		/** ======
		 * 【关键点】 jdk动态代理的诞生！Proxy是jdk的动态代理生成工具类
		 *  第一个参数是类加载器（目标类）
		 *  第二个参数是目标类实现的接口（含系统接口）（数组）
		 *  第三个是InvocationHandler，也就是这个JdkDynamicAopProxy自己，它实现了handler接口
		 *  而前面的步骤，把筛出来的 一堆排好序的 advisor ，给了这货
		 *  未来真正调用代理方法的时候，这货的invoke被触发，开始使用这些advisor当责任链列表，一环环被调用……
		 *  而这，就是aop的真相~  其实很简单！
		 */
		// ###### this实现了InvocationHandler
		return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this); // ====== 真身被隐藏，proxy被创建！
	}

	/**
	 * Determine whether the JDK bootstrap or platform loader has been suggested ->
	 * use higher-level loader which can see Spring infrastructure classes instead.
	 */
	private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
		if (classLoader == null) {
			// JDK bootstrap loader -> use spring-aop ClassLoader instead.
			return getClass().getClassLoader();
		}
		if (classLoader.getParent() == null) {
			// Potentially the JDK platform loader on JDK 9+
			ClassLoader aopClassLoader = getClass().getClassLoader();
			ClassLoader aopParent = aopClassLoader.getParent();
			while (aopParent != null) {
				if (classLoader == aopParent) {
					// Suggested ClassLoader is ancestor of spring-aop ClassLoader
					// -> use spring-aop ClassLoader itself instead.
					return aopClassLoader;
				}
				aopParent = aopParent.getParent();
			}
		}
		// Regular case: use suggested ClassLoader as-is.
		return classLoader;
	}

	/**
	 * Finds any {@link #equals} or {@link #hashCode} method that may be defined
	 * on the supplied set of interfaces.
	 * @param proxiedInterfaces the interfaces to introspect
	 */
	private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
		// ###### 判断被代理的接口中是否定义了equals()、hashCode()方法，如果程序员在接口中手动定义了这两个方法，则也会进行代理
		// ###### 否则这两个方法是不会走代理逻辑的
		for (Class<?> proxiedInterface : proxiedInterfaces) {
			Method[] methods = proxiedInterface.getDeclaredMethods();
			for (Method method : methods) {
				if (AopUtils.isEqualsMethod(method)) {
					this.equalsDefined = true;
				}
				if (AopUtils.isHashCodeMethod(method)) {
					this.hashCodeDefined = true;
				}
				if (this.equalsDefined && this.hashCodeDefined) {
					return;
				}
			}
		}
	}


	/**
	 * Implementation of {@code InvocationHandler.invoke}.
	 * <p>Callers will see exactly the exception thrown by the target,
	 * unless a hook method throws an exception.
	 */
	/* ======
	   参数：
	   代理对象
	   目标方法
	   方法参数
	*/
	/* ======
		目标
	    1、调用增强方法
	    2、反射调用目标方法
	    重点关注：getInterceptorsAndDynamicInterceptionAdvice责任链方法
		和invocation.proceed()方法（开始调用）
	 */
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// ====== 一堆判断和加载，拖到最下面，看责任链部分，核心在那里
		Object oldProxy = null;
		boolean setProxyContext = false;
		//  获取到我们的目标对象
		// ====== 获取目标资源，此处就是slaverImpl，debug变量值试试
		// ###### 拿到被代理对象
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try { // ====== 一顿if猛如虎,一看跟业务没半毛钱关系,往下走！
			// ###### equals方法不代理
			// 若是equals方法不需要代理
			// ====== 当前方法是否等于Equals,如果是,则重写
			// ###### 如果接口中没有定义equals()方法，那么则直接调用，不走代理
			//equals方法的处理
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
				// ====== 当前方法是否等于HashCode,如果是，则重写
			}
			// ###### hashCode方法不代理
			// 若是hashCode方法不需要代理
			//hash方法的处理
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
				// ====== 方法声明类是否等于DecoratingProxy
			}
			// ###### 方法来自DecoratingProxy接口的也不代理
			//  若是DecoratingProxy也不要拦截器执行
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// ====== 使用反射直接进行调用目标资源
				// ###### 得到代理对象的类型，而不是所实现的接口
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);				
			}
			// ###### 目标对象本身实现了Advised接口的也不代理
			// ====== 方法声明是否接口 、或者是否Advised类型
			// isAssignableFrom方法：如果调用这个方法的class或接口与参数cls表示的类或接口相同，或者是参数cls表示的类或接口的父类，则返回true
			/*
             * Class类的isAssignableFrom(Class cls)方法:
             * 如果调用这个方法的class或接口 与 参数cls表示的类或接口相同，
             * 或者是参数cls表示的类或接口的父类，则返回true。
             * 形象地：自身类.class.isAssignableFrom(自身类或子类.class)  返回true
             *   例：
             *   System.out.println(ArrayList.class.isAssignableFrom(Object.class));//false
             *   System.out.println(Object.class.isAssignableFrom(ArrayList.class));//true
             */
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// ====== 用代理配置对ProxyConfig进行服务调用
				// ###### 也是直接调用Advised接口中的方法，不走代理逻辑
				// ###### 其实就是利用代理对象获取ProxyFactory中的信息
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			/** 
			 * 这个配置是暴露我们的代理对象到线程变量中，需要搭配@EnableAspectJAutoProxy(exposeProxy = true)一起使用
			 * 比如在目标对象方法中再次获取代理对象可以使用这个AopContext.currentProxy()
			 * 还有的就是事务方法调用事务方法的时候也是用到这个
			 */
			// ====== 如果暴露；就放到ThreadLocal,默认不暴露，不重要，不管他
			// ###### 如果ProxyFactory的exposeProxy为true,则将代理对象设置到currentProxy这个ThreadLocal中去
			// 有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理
			if (this.advised.exposeProxy) {
				//  把我们的代理对象暴露到线程变量中
				// ====== 放到ThreadLocal
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			//  获取我们的目标对象
			// ====== 目标资源；对应业务实现类，这是我们的真身（SlaverImpl），debug变量看看？
			// ###### 被代理对象和代理类
			target = targetSource.getTarget();
			//  获取我们目标对象的class
			// ====== 目标类；对应业务实现类
			Class<?> targetClass = (target != null ? target.getClass() : null);
			// ###### 根据当前执行的方法获取要执行的增强器,并以列表返回(链的思想)
			// 从Advised中根据方法名和目标类获取AOP拦截器执行链
			// ====== 【关键点1】构建责任链，看一下它的值是什么？（前面整理过我们的切面方法）
			// ###### 代理对象在执行某个方法时，根据方法筛选出匹配的Advisor,并适配成Interceptor
			// 获取当前方法的拦截器链
			// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// 如果拦截器链为空
			// ====== 如果为空，则直接调用target的method
			// Check whether we have any advice. If we don't, we can fall back on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// 通过反射直接调用执行
				// ====== 如果没有拦截器,直接调用
				// ###### 如果没有Advice，则直接调用对应方法
				// 如果没有发现任何拦截器那么直接调用切点方法(目标方法)
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				//  如果没有发现任何拦截器那么直接调用切点方法
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				//  将拦截器封装在ReflectiveMethodInvocation，以便于使用其proceed进行处理
				/** ======
					 proxy代理对象
					 target是目标类
					 targetClass是目标类的class
					 method是目标方法
					 args是对应的方法参数
					 chain是对应的拦截器
				 */
				//将拦截器封装在ReflectiveMethodInvocation，
				//以便于使用其proceed进行链接表用拦截器
				// We need to create a method invocation...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// ###### 构造增强器链,执行增强器的逻辑
				// ====== 责任链即将开始调用的入口~~~
				// 执行拦截器链
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed(); // ===> debug进去！【关键点2】
			}
			// 获取返回类型
			// ====== 以下方法，在责任链执行完成后，才会跑到，就一些收尾工作
			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			//返回结果
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			// 返回值类型错误
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			// ###### 返回值的处理
			return retVal;
		}
		finally {
			/* 
			   如果目标对象不为空且目标对象是可变的,如prototype类型
			   通常我们的目标对象都是单例的,即targetSource.isStatic为true
			*/
			if (target != null && !targetSource.isStatic()) {
				//  释放目标对象
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				//  线程上下文复位
				// ====== 重新设置下ThreadLocal
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}


	/**
	 * Equality means interfaces, advisors and TargetSource are equal.
	 * <p>The compared object may be a JdkDynamicAopProxy instance itself
	 * or a dynamic proxy wrapping a JdkDynamicAopProxy instance.
	 */
	@Override
	public boolean equals(@Nullable Object other) {
		if (other == this) {
			return true;
		}
		if (other == null) {
			return false;
		}

		JdkDynamicAopProxy otherProxy;
		if (other instanceof JdkDynamicAopProxy) {
			otherProxy = (JdkDynamicAopProxy) other;
		}
		else if (Proxy.isProxyClass(other.getClass())) {
			InvocationHandler ih = Proxy.getInvocationHandler(other);
			if (!(ih instanceof JdkDynamicAopProxy)) {
				return false;
			}
			otherProxy = (JdkDynamicAopProxy) ih;
		}
		else {
			// Not a valid comparison...
			return false;
		}

		// If we get here, otherProxy is the other AopProxy.
		return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
	}

	/**
	 * Proxy uses the hash code of the TargetSource.
	 */
	@Override
	public int hashCode() {
		return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
	}

}
